package org.wisdom.ab.apis.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import org.wisdom.ab.apis.StaffController;
import org.wisdom.ab.apis.assembler.StaffAssembler;
import org.wisdom.ab.domain.staff.entity.AbStaff;
import org.wisdom.ab.domain.staff.entity.AbStaffDepartment;
import org.wisdom.ab.domain.staff.repository.StaffRepository;
import org.wisdom.ab.dto.StaffDto;
import org.wisdom.annotation.WisdomController;
import org.wisdom.exception.BusinessException;
import org.wisdom.vo.PageModel;
import org.wisdom.vo.ResponseResult;

import lombok.RequiredArgsConstructor;

/**
 * 成员管理API
 *
 * @author 张智研
 * @date 2022/05/16 08:08
 **/
@RequiredArgsConstructor
@WisdomController
public class StaffControllerImpl implements StaffController {

    private final StaffRepository staffRepository;
    private final StaffAssembler staffAssembler;

    @Override
    public ResponseResult<String> save(StaffDto staffDto) {
        AbStaff user = this.staffAssembler.staffDtoToEntity(staffDto);
        user = this.staffRepository.save(user);
        return ResponseResult.ok(user.getId());
    }

    @Override
    public ResponseResult<StaffDto> get(String staffId) {
        Optional<AbStaff> userOptional = this.staffRepository.findById(staffId);
        if (userOptional.isPresent()) {
            StaffDto staffDto = this.staffAssembler.staffEntityToDto(userOptional.get());
            return ResponseResult.ok(staffDto);
        } else {
            throw new BusinessException("成员{" + staffId + "}不存在！");
        }
    }

    @Override
    public ResponseResult<String> delete(String staffIds) {
        List<String> ids = Arrays.stream(staffIds.split(",")).collect(Collectors.toList());
        this.staffRepository.deleteAllById(ids);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult<PageModel<StaffDto>> list(StaffDto staffDto) {
        Specification<AbStaff> querySpec = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasLength(staffDto.getId())) {
                predicates.add(criteriaBuilder.equal(root.get("id").as(String.class), staffDto.getId()));
            }
            if (StringUtils.hasLength(staffDto.getStaffCode())) {
                predicates.add(
                    criteriaBuilder.like(root.get("staffCode").as(String.class), "%" + staffDto.getStaffCode() + "%"));
            }
            if (StringUtils.hasLength(staffDto.getStaffName())) {
                predicates.add(
                    criteriaBuilder.like(root.get("staffName").as(String.class), "%" + staffDto.getStaffName() + "%"));
            }
            if (StringUtils.hasLength(staffDto.getMobile())) {
                predicates
                    .add(criteriaBuilder.like(root.get("mobile").as(String.class), "%" + staffDto.getMobile() + "%"));
            }
            if (StringUtils.hasLength(staffDto.getEmail())) {
                predicates
                    .add(criteriaBuilder.like(root.get("email").as(String.class), "%" + staffDto.getEmail() + "%"));
            }
            if (StringUtils.hasLength(staffDto.getCompanyId())) {
                predicates.add(criteriaBuilder.equal(root.get("companyId").as(String.class), staffDto.getCompanyId()));
            }
            if (StringUtils.hasLength(staffDto.getCompanyId())) {
                predicates.add(criteriaBuilder.equal(root.get("companyId").as(String.class), staffDto.getCompanyId()));
            }
            if (staffDto.getDepartment() != null && staffDto.getDepartment().length > 0) {
                Subquery<AbStaffDepartment> sq = criteriaQuery.subquery(AbStaffDepartment.class);
                Root<AbStaffDepartment> root1 = sq.from(AbStaffDepartment.class);
                Predicate p1 = criteriaBuilder.equal(root1.get("staffId"), root.get("id"));
                Predicate p2 = criteriaBuilder.equal(root1.get("deptId"), staffDto.getDepartment()[0]);
                sq.select(root1).where(criteriaBuilder.and(p1, p2));
                predicates.add(criteriaBuilder.exists(sq));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        if (staffDto.getIsPage()) {
            Pageable pageable = PageRequest.of(staffDto.getPageNumber() - 1, staffDto.getPageSize(), Sort.unsorted());
            Page<AbStaff> staffPage = this.staffRepository.findAll(querySpec, pageable);
            return ResponseResult.ok(this.toPageModel(staffPage));
        } else {
            List<AbStaff> userList = this.staffRepository.findAll(querySpec);
            List<StaffDto> staffDtoList = this.staffAssembler.staffEntityToDto(userList);
            PageModel<StaffDto> pageModel = new PageModel<>();
            pageModel.setContent(staffDtoList);
            pageModel.setTotalPages(staffDtoList.size());
            pageModel.setTotalElements(staffDtoList.size());
            pageModel.setPageNumber(1);
            pageModel.setPageSize(1);
            return ResponseResult.ok(pageModel);
        }
    }

    @Override
    public ResponseResult<List<StaffDto>> listDepartmentUser(String departmentId) {
        List<AbStaff> userList = this.staffRepository.findAllByDeptId(departmentId);
        List<StaffDto> staffDtoList = this.staffAssembler.staffEntityToDto(userList);
        return ResponseResult.ok(staffDtoList);
    }

    /**
     * 封装结果集
     *
     * @author 张智研
     * @date 2023/05/19 11:21
     * @param staffPage
     *            成员分页
     * @return org.wisdom.vo.PageModel<org.wisdom.ab.dto.StaffDto>
     */
    private PageModel<StaffDto> toPageModel(Page<AbStaff> staffPage) {
        PageModel<StaffDto> pageModel = new PageModel<>();
        List<StaffDto> staffDtoList = this.staffAssembler.staffEntityToDto(staffPage.getContent());
        pageModel.setContent(staffDtoList);
        pageModel.setTotalPages(staffPage.getTotalPages());
        pageModel.setTotalElements(staffPage.getTotalElements());
        pageModel.setPageNumber(staffPage.getNumber() + 1);
        pageModel.setPageSize(staffPage.getSize());
        return pageModel;
    }
}
